/*
 *
 *  *    Copyright © OpenAtom Foundation.
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *         http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.inspur.edp.commonmodel.engine.core.data.serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.manager.serialize.NestedSerializeContext;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.spi.jsonser.entity.AbstractEntitySerializerItem;
import com.inspur.edp.cef.spi.jsonser.util.SerializerUtil;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.commonmodel.engine.api.data.AssoInfoJsonSerializer;
import com.inspur.edp.commonmodel.engine.api.data.AssociationInfo;
import com.inspur.edp.commonmodel.engine.api.data.IEngineEntityData;
import com.inspur.edp.commonmodel.engine.core.exception.CMEngineCoreException;
import com.inspur.edp.commonmodel.engine.core.exception.ErrorCodes;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import org.locationtech.jts.geom.Geometry;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;

public class CommonEngineDataSerializerItem extends AbstractEntitySerializerItem {

    public CommonEngineDataSerializerItem() {
    }

    @Override
    public void writeEntityBasicInfo(JsonGenerator writer, IEntityData data,
                                     SerializerProvider serializer) {
        if(!(data instanceof IEngineEntityData)){
            throw new CMEngineCoreException(ErrorCodes.CM_ENGINE_1024, (data == null ? "null" : data.getClass().getTypeName()));
        }
        IEngineEntityData engineEntityData = (IEngineEntityData) data;
        IGspCommonObject commonObject = engineEntityData.getNode();

        for (IGspCommonField field : commonObject.getContainElements()) {
            String name = field.getLabelID();
            Object value = data.getValue(name);
            if (value instanceof Date) {
                if (field.getMDataType() == GspElementDataType.Date) {
                    this.writeDate(writer, value, name, serializer);
                } else{
                    this.writeDateTime(writer, value, name, serializer);
                }
            } else if (value instanceof String) {
                this.writeString(writer, value, name, serializer);
            } else if (value instanceof Integer) {
                this.writeInt(writer, value, name, serializer);
            } else if (value instanceof Boolean) {
                this.writeBool(writer, value, name, serializer);
            } else if (value instanceof BigDecimal) {
                this.writeDecimal(writer, value, name, serializer, (field.isBigNumber() || getCefSerializeContext().isBigNumber()));
            } else if (value instanceof Byte[]) {
                this.writeBytes(writer, value, name, serializer);
            } else if (value instanceof Geometry) {
                this.writeGeometry(writer, value, name, serializer);
            } else if (value instanceof AssociationInfo) {
                writeAssociationInfo(writer, (AssociationInfo) value, name, serializer);
            } else {
                //如果是浮点UDT，且开启大数字 或者是去粘性序列化
                if(field.getIsUdt() && field.getMDataType() == GspElementDataType.Decimal && (field.isBigNumber() || getCefSerializeContext().isBigNumber())){
                    NestedSerializeContext context = new NestedSerializeContext();
                    context.setBigNumber(true);
                    String configId = getUdtConfigId(field.getUdtID());
                    SerializerUtil.writeNestedValue(writer, serializer, configId, (ICefData)value, field.getLabelID(), context);
                }
                else{
                    try {
                        writer.writeFieldName(StringUtils.toCamelCase(name));
                        String valueTemp = getDefaultMapper().writeValueAsString(value);
                        writer.writeRawValue(valueTemp);
                    } catch (IOException var7) {
                        throw new CMEngineCoreException(var7);
                    }
                }
            }
        }
    }

    private String getUdtConfigId(String udtId) {
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(udtId);
        return udt.getUdtType();
    }

    private boolean isSerializeDate(IGspCommonObject node, String name) {
        if (node.getContainElements().getByLabelId(name) == null
                || node.getContainElements().getByLabelId(name).getMDataType() == GspElementDataType.Date)
            return true;
        return false;
    }

    private volatile static ObjectMapper mapper;
    private static Object mapperLock = new Object();

    private static ObjectMapper getDefaultMapper() {
        if (mapper == null) {
            synchronized (mapperLock) {
                if (mapper == null) {
                    mapper = new ObjectMapper();
                }
            }
        }
        return mapper;
    }

    private static AssoInfoJsonSerializer assoSerializer = new AssoInfoJsonSerializer();

    private void writeAssociationInfo(JsonGenerator writer, AssociationInfo value, String name,
                                      SerializerProvider serializer) {
        try {
            if (value.getAssociation().isKeepAssoPropertyForExpression() && "1".equals(SerializerUtil.isKeepAssoPropertyForExpression().get())) {
                writer.writeFieldName(StringUtils.toCamelCase(name));
                writer.writeString(value.getValue());

                writer.writeFieldName(StringUtils.toCamelCase(name) + "___AssoExt");
                assoSerializer.serialize(value, writer, serializer);
            } else {
                writer.writeFieldName(StringUtils.toCamelCase(name));
                assoSerializer.serialize(value, writer, serializer);
            }
        } catch (IOException e) {
            throw new CMEngineCoreException(e, ErrorCodes.CM_ENGINE_1005);
        }
    }

    @Override
    public boolean readEntityBasicInfo(
            JsonParser reader, DeserializationContext serializer, IEntityData data,
            String propertyName) {
        return false;
    }

    @Override
    public boolean writeModifyPropertyJson(
            JsonGenerator writer, String propertyName, Object value, SerializerProvider serializer) {
        //变更集的序列化, 0227与大家讨论后一致决定, 在序列化基类中对于seritem不处理的属性执行默认序列化, 所以此处
        //可以直接return false交由序列化基类处理.
        return false;
    }

    @Override
    public Object readModifyPropertyValue(
            JsonParser reader,
            DeserializationContext serializer,
            RefObject<String> propertyName,
            RefObject<Boolean> hasRead) {
        hasRead.argvalue = false;
        return null;
    }
}
